/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simulator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import net.octopeer.core.*;
import net.octopeer.message.*;

/**
 *
 * @author andria
 */
public class Simulator {
//    public ArrayList<String> eventList;
//    public ArrayList<ArrayList> timeList;
    public Peer[] peerList;
    public Resource[] resourceList;
//    public String msgStrList = "";
    public ArrayList<String> reqStrArrList = new ArrayList();
    public String reqStrList = "";
    public ArrayList<String> repStrArrList = new ArrayList();
    public String repStrList = "";
    public String outStr = "";
//    public String dupStrList = "";
    
    public Simulator(int pSize, int rSize) {
        peerList = new Peer[pSize];  
        resourceList = new Resource[pSize * rSize];
    }

    public void preProcess(Peer peer) {
//        System.out.println("PreProcess: " + peer.getNode().getNodeIPaddress());
        // algorithm 2: sort neighbor list based on rank
        ArrayList<Neighbor> temp = peer.getNeighborList();
        Collections.sort(temp, new NeighborComparator());
        peer.setNeighborList(temp);
    }
    
    public void reviewMessage(Peer peer) throws Exception {
        if (!peer.message.equals("")) {
            String[] msg = peer.message.split(" ");

            if (msg[0].equals("request")) {
                requestQuery(peer, msg[1]);
            } else if (msg[0].equals("respond")) {
                queryReplay(peer, msg[1]);
            } else {
                System.out.println("unformatted message");
            }
        }
    }
    
    public void requestQuery(Peer peer, String reqString) throws Exception {
        // algorithm 3;
        Request req = new Request();
        req = req.fromXMLString(reqString);
        int resIdx, cacIdx, i;
        
        if (peer.findRequestIDIdx(req.getReqID()) == -1) {
            peer.addRequestID(req.getReqID());
            
            resIdx = checkResourceIdx(peer, req);
            if (resIdx == -1) {
                cacIdx = checkCacheIdx(peer, req);
                if (cacIdx == -1) {
                    preProcess(peer);
                    i = 0;
                    while ((i < 4) && (i < peer.getNeighborList().size())) {
                        PeerNode node = peer.getNeighborList().get(i).getNode();
                        req.addPathPeer(node);
                        String reqStr = "request " + req.toXMLString();
                        // send to ... node;
                        sendMessage(peer, node, reqStr);
//                        System.out.println("Request forwarded to : " + node.getNodeIPaddress());
                        reqStrList += "x";
                        i++;
                    }
                } else {
                    PeerNode node = peer.getCacheList().get(cacIdx).getNodeList().get(0);
                    req.addPathPeer(node);
                    String reqStr = "request " + req.toXMLString();
                    // send to ... node;
                    sendMessage(peer, node, reqStr);
//                    System.out.println("Request forwarded to : " + node.getNodeIPaddress());
                    reqStrList += "x";
                }
            } else {
                //replay
                Respond rep = new Respond(req.getReqID(), peer.getResourceList().get(resIdx), 
                        peer.getNode(), req.getPathPeers());

                PeerNode node = null;
                if (req.getPathPeers().size() > 2) {
                    node = (PeerNode) req.getPathPeers().get(req.getPathPeers().size()-2);
                } else {
                    node = (PeerNode) req.getPathPeers().get(req.getPathPeers().size()-1);
                }
                
                String repStr = "respond " + rep.toXMLString();
                // send to ... node;
                sendMessage(peer, node, repStr);
//                System.out.println("Request found, Respond forwarded to : " + node.getNodeIPaddress());
                repStrList += "o";
            }
        } else {
//            System.out.println("Request droped, duplicate request");
        }
        
    }
    
    public void queryReplay(Peer peer, String repString) throws Exception {
        // algorithm 4;
        Respond rep = new Respond();
        rep = rep.fromXMLString(repString);
        ArrayList<PeerNode> path = rep.getPathPeers();
        Cache resCache = null;
        
        peer.addNeighborNode((PeerNode) path.get(path.size()-1));
        
        if (rep.getPathPeers().get(0).getNodeID().equals(peer.getNode().getNodeID())) {
            // peer = request node
//            System.out.println("Respond received");
//            reqStrList += "\n";
//            repStrList += "\n";
            
            // add cache
            resCache = new Cache(rep.getResDetail(), rep.getResNode());
            peer.addCache(resCache);
            
            // add neighbor
            peer.addNeighborNode(rep.getResNode());
            
        } else {
            // add cache
            resCache = new Cache(rep.getResDetail(), rep.getResNode());
            peer.addCache(resCache);
            
            // add neighbor
            peer.addNeighborNode(rep.getResNode());
            
            rep.remPathPeer();
            PeerNode node = (PeerNode) rep.getPathPeers().get(rep.getPathPeers().size()-2);
            String repStr = "respond " + rep.toXMLString();
            // send to ... node;
            sendMessage(peer, node, repStr);
//            System.out.println("Respond forwarded to : " + node.getNodeIPaddress());
            repStrList += "o";
        }
    }
    
    public void postProcess(Peer peer) {
        // algorithm 5;
    }
    
    
    public int checkResourceIdx(Peer peer, Request req) {
        int result = -1;
        Resource res = null;
        ArrayList<Resource> resourceList = peer.getResourceList();
        int i = 0;
        while (i < resourceList.size()) {
            res = (Resource) resourceList.get(i);
            if ((res.getResourceType().equals(req.getReqType())) &&
                (res.getResourceDetail().equals(req.getReqKeyword()))){
                result = i;
                break;
            }
            i++;
        }
        
        return result;
    }
    
    public int checkCacheIdx(Peer peer, Request req) {
        int result = -1;
        Resource res = null;
        ArrayList<Cache> cacheList = peer.getCacheList();
        int i = 0;
        while (i < cacheList.size()) {
            res = (Resource) cacheList.get(i).getResource();
            if ((res.getResourceType().equals(req.getReqType())) &&
                (res.getResourceDetail().equals(req.getReqKeyword()))){
                result = i;
                break;
            }
            i++;
        }
        
        return result;
    }
    
    public void sendMessage(Peer peer, PeerNode pNode, String msg) {
        int nodeIdx = findNodeIdx(pNode);
        peerList[nodeIdx].message = msg;
        peer.message = "";
    }
    
    public int findNodeIdx(PeerNode pNode) {
        int result = -1;
        int i = 0;
        
        while (i<peerList.length) {
            PeerNode temp = (PeerNode) peerList[i].getNode();
            if (temp.getNodeID().equals(pNode.getNodeID())) {
                result = i;
                break;
            }
            i++;
        }
        
        return result;
    }
    
    
    
    public static void main(String[] args) throws Exception {
        int pSize = Integer.parseInt(args[0]);
        int rSize = Integer.parseInt(args[1]);
        int reqLoop = Integer.parseInt(args[2]);
        int maxTTL = Integer.parseInt(args[3]);
        
        Simulator s = new Simulator(pSize, rSize);
        int i, j, k;
        PeerNode pn = null;
        Resource res = null;
        Neighbor neg = null;
        Request req = null;
        
        int pIdx, rIdx;
        
        // init resourceList
        for (i = 0; i < pSize; i++) {
            for (j = 0; j < rSize; j++) {
                s.resourceList[(i*rSize)+j] = new Resource("res", Integer.toString(i) + Integer.toString(j));
            }
        }
        
        // init peerList
        for (i = 0; i < pSize; i++) {
            pn = new PeerNode(Integer.toString(i), 9090);
            s.peerList[i] = new Peer(pn);
        }
        
        // assign resources
        for (i = 0; i < pSize; i++) {
            for (j = 0; j < rSize; j++) {
                s.peerList[i].addResource(s.resourceList[(i*rSize)+j]);
            }
        }
        
        // assign neighbor
        for (i = 0; i < pSize; i++) {
            if (i == pSize -1) {
                s.peerList[i].addNeighborNode(s.peerList[0].getNode());
            } else {
                s.peerList[i].addNeighborNode(s.peerList[i+1].getNode());
            }
        }
        
        Random generator = new Random();
        
        for (i = 0; i < reqLoop; i++) {
            s.reqStrList = "";
            s.repStrList = "";
            pIdx = generator.nextInt(pSize);
            rIdx = generator.nextInt(rSize);
            req = new Request("res", s.resourceList[rIdx].getResourceDetail(), 
                    s.peerList[pIdx].getNode(), maxTTL, s.peerList[pIdx].getNode());
            
            s.peerList[pIdx].message = "request " + req.toXMLString();
            
            for (j=0; j < maxTTL; j++) {
                for (k = 0; k < pSize; k++) {
                    Peer work = (Peer)s.peerList[k];
                    s.reviewMessage(work);
                }
            }
             s.reqStrArrList.add(s.reqStrList);
            s.repStrArrList.add(s.repStrList);
        }
        

        
        System.out.println("Request messages: ");
        i = 0;
        while (i < s.reqStrArrList.size()) {
             s.outStr = (String) s.reqStrArrList.get(i);
             System.out.println(s.outStr);
             i++;
        }
        
        System.out.println("\n\nRespond messages: ");
        i = 0;
        while (i < s.repStrArrList.size()) {
             s.outStr = (String) s.repStrArrList.get(i);
             System.out.println(s.outStr);
             i++;
        }
        
    }
}
